<template>
  <div class="websocket-wrap">
    <div class="nav">
      当前连接状态: {{ connectStatus }}，当前在线人数: {{ onlineCount }}
    </div>
    <div class="main">
      <div
        ref="contentList"
        class="content-list"
      >
        <div
          v-for="(item, index) in chatList"
          :key="index"
          class="item"
        >
          <div
            v-if="item.type === webSocketMsgType.userInRoom"
            class="tip"
          >
            {{ item.nickname }}加入了聊天({{ item.time }})
          </div>
          <div
            v-if="item.type === webSocketMsgType.userOutRoom"
            class="tip"
          >
            {{ item.nickname }}退出了聊天({{ item.time }})
          </div>
          <div v-if="item.type === webSocketMsgType.userSendMsg">
            <div :class="{ 'msg-item': true, 'is-me': item.id === wsId }">
              <img
                :src="item.avatar"
                class="avatar"
                alt=""
              >
              <div class="info">
                <div
                  v-if="item.id !== wsId"
                  class="nickname"
                >
                  {{ item.nickname }}
                </div>
                <div class="msg">
                  {{ item.msg }}
                  <i class="triangle" />
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div
        v-if="!isJoin"
        class="mask"
      />

      <div
        v-if="!isJoin"
        class="join"
      >
        <div class="item avatar">
          <el-popover
            popper-class="popover"
            placement="bottom"
            width="200"
            trigger="click"
          >
            <div class="avatar-list">
              <img
                v-for="(item, index) in avatarList"
                :key="index"
                :src="item"
                class="avatar"
                alt=""
                @click="avatar = item"
              >
            </div>
            <slot name="reference">
              <div class="curr-avatar">
                <img
                  width="50"
                  class="avatar"
                  :src="avatar"
                  alt=""
                >
                <el-icon class="bottom">
                  <CaretBottom />
                </el-icon>
              </div>
            </slot>
          </el-popover>
        </div>
        <div class="item input">
          <div>
            <el-input
              v-model="nickname"
              maxlength="6"
              placeholder="请输入昵称"
            />
          </div>
        </div>
        <div
          class="item btn"
          @click="join"
        >
          加入
        </div>
      </div>
      <div
        v-else
        class="send-msg"
      >
        <el-input
          v-model="msg"
          placeholder="请输入内容"
        >
          <template #append>
            <el-button @click="userSendMsg">
              发送
            </el-button>
          </template>
        </el-input>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, inject, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { io } from 'socket.io-client'
const webSocketMsgType = ref({
  connect: 'connect', // 用户连接
  userInRoom: 'userInRoom', // 用户进入聊天
  userOutRoom: 'userOutRoom', // 用户退出聊天
  userSendMsg: 'userSendMsg', // 用户发送消息
  heartbeatCheck: 'heartbeatCheck', // 心跳检测
  getOnlineUser: 'getOnlineUser' // 获取在线用户
})
const connectStatusEnum = ref({
  connecting: 'connecting', // 连接中
  connected: 'connected', // 已连接
  disconnect: 'disconnect', // 断开连接
  reconnect: 'reconnect' // 重新连接
})
const avatarList = ref([
  'http://img.cdn.ceart.top/user/Billd.webp',
  'http://img.cdn.ceart.top/user/Hololo.webp',
  'http://img.cdn.ceart.top/user/MoonTIT.webp',
  'http://img.cdn.ceart.top/user/CoCo.webp',
  'http://img.cdn.ceart.top/user/Nill.webp',
  'http://img.cdn.ceart.top/user/Ojin.webp'
])

const $newmessage:any = inject('$newmessage')

const connectStatus = ref(connectStatusEnum.value.disconnect) // 是否连接
const chatList:any = ref([])
const wsInstance:any = ref(null)// ws实例
const nickname = ref('')
const wsId = ref(null)
const avatar = ref(avatarList.value[1])
const isJoin = ref(false) // 是否已加入聊天
const msg = ref('')
const onlineCount = ref(0)// 当前在线人数
const url = ref('wss://www.hsslive.cn/')// 当前在线人数

const contentList:any = ref(null)

watch(() => chatList, (newVal, oldVal) => {
  nextTick(() => {
    contentList.value.scrollTop = contentList.value.scrollHeight
  })
})

onMounted(() => {
  createWebSocket(url.value)
})

onUnmounted(() => {
  closeWs()
})

const join = () => {
  if (nickname.value.length > 6) {
    return $newmessage('昵称最多6个字符!', 'info')
  }
  isJoin.value = true
  wsInstance.value.emit(webSocketMsgType.value.userInRoom, {
    nickname: nickname.value,
    msg: msg.value
  })
}
// 关闭websocket连接
const closeWs = () => {
  connectStatus.value = connectStatusEnum.value.disconnect
  wsInstance.value.close()
}
// 处理收到的消息
const handleReceiveMessage = () => {
  wsInstance.value.on(webSocketMsgType.value.getOnlineUser, (data:any) => {
    onlineCount.value = data.count
  })
  wsInstance.value.on(webSocketMsgType.value.userInRoom, (data:any) => {
    const { nickname, msg, time } = data
    chatList.value.push({
      type: webSocketMsgType.value.userInRoom,
      nickname,
      msg,
      time
    })
  })
  wsInstance.value.on(webSocketMsgType.value.userOutRoom, (data:any) => {
    const { nickname, time } = data
    chatList.value.push({
      type: webSocketMsgType.value.userOutRoom,
      nickname,
      time
    })
  })
  wsInstance.value.on(webSocketMsgType.value.userSendMsg, (data:any) => {
    const { id, nickname, avatar, msg, time } = data
    chatList.value.push({
      type: webSocketMsgType.value.userSendMsg,
      id,
      nickname,
      avatar,
      msg,
      time
    })
  })
}
// 新建WebSocket
const createWebSocket = (url:string) => {
  try {
    if ('WebSocket' in window) {
      wsInstance.value = io(url, { transports: ['websocket'] })
      initWebSocket()
    } else {
      console.log('你的浏览器不支持WebSocket！')
    }
  } catch (error) {
    console.log('新建WebSocket出错', error)
  }
}
// 初始化
const initWebSocket = () => {
  wsInstance.value.on('connect', () => {
    wsId.value = wsInstance.value.id
    connectStatus.value = connectStatusEnum.value.connected
    wsInstance.value.emit(webSocketMsgType.value.getOnlineUser, {})
    handleReceiveMessage()
  })
  wsInstance.value.on('disconnect', (reason:string) => {
    if (reason === 'io server disconnect') {
      // the disconnection was initiated by the server, you need to reconnect manually
      wsInstance.value.connect()
    }
    console.log('断开websocket连接!')
  })
  wsInstance.value.on('connect_error', () => {
    console.log('连接websocket错误，开始重连！')
    wsInstance.value.connect()
  })
}
// 用户发送消息
const userSendMsg = () => {
  if (msg.value.trim() === '') {
    return $newmessage('请输入内容', 'info')
  }
  if (msg.value.length > 100) {
    return $newmessage('最多输入100个字符', 'info')
  }
  wsInstance.value.emit(webSocketMsgType.value.userSendMsg, {
    nickname: nickname.value,
    avatar: avatar.value,
    msg: msg.value
  })
  msg.value = ''
}
</script>

<style lang="scss" scoped>
@import '@/assets/css/constant.scss';
@import '@/assets/css/mixin.scss';
.websocket-wrap {
  .main {
    position: relative;
    .content-list {
      height: 400px;
      overflow-y: scroll;
      background-color: $theme-color3;
      @extend .hideScrollbar;
      .item {
        padding: 0 10px;
        margin-bottom: 10px;
      }
      .tip {
        text-align: center;
        margin: 10px 0;
      }
      .msg-item {
        display: flex;
        &.is-me {
          flex-direction: row-reverse;
          .info {
            .msg {
              background-color: $theme-color11;
              .triangle {
                left: auto;
                right: -5px;
                top: 12px;
              }
            }
          }
        }
        .avatar {
          width: 40px;
          height: 40px;
        }
        .info {
          margin: 0 10px;

          .nickname {
            font-size: 14px;
          }
          .msg {
            position: relative;
            max-width: 200px;
            border-radius: 5px;
            word-break: break-all;
            margin-top: 4px;
            background-color: $theme-color6;
            padding: 10px;
            .triangle {
              width: 10px;
              height: 10px;
              background-color: inherit;
              position: absolute;
              left: -5px;
              right: auto;
              top: 15px;
              transform: rotate(45deg);
            }
          }
        }
      }
    }
    .send-msg {
      display: flex;
    }
    .mask {
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background-color: $theme-color3;
    }
    .join {
      position: absolute;
      width: 100%;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);

      .item {
        margin: 0 auto;
        &.avatar {
          width: 50px;
        }
        &.input {
          margin-top: 20px;
          width: 30%;
        }
        &.btn {
          margin-top: 20px;
          width: 60px;
          height: 60px;
          color: $theme-color6;
          line-height: 60px;
          border-radius: 50%;
          text-align: center;
          background-color: $theme-color1;
          cursor: pointer;
        }
        .curr-avatar {
          position: relative;
          .bottom {
            position: absolute;
            bottom: 0;
            right: 0;
          }
        }
        .avatar {
          width: 50px;
          border-radius: 50%;
          cursor: pointer;
        }
      }
    }
  }
}
</style>

<style lang="scss">
.popover {
  width: 200px;
  .avatar-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
  }

  .avatar {
    width: 30%;
    border-radius: 50%;
    margin-bottom: 10px;
    cursor: pointer;
  }
}
</style>
